from django.http import HttpResponse, HttpResponseServerError
from django.shortcuts import render
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
from tool.models import Tool
from django.http import JsonResponse
from django.core.files.storage import FileSystemStorage
import pandas as pd
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import seaborn as sns
import os
import csv
from django.shortcuts import render
from django.http import HttpResponse
from django.conf import settings


# Create your views here.
def index(request):
    return render(request, '../templates/index.html')

def dsptool(request):
    if request.FILES.get('upload_file'):
        uploaded_file = request.FILES['upload_file']
        df = pd.read_csv(uploaded_file)

        # 特征工程
        features = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
        X = df[features].values

        # 数据标准化
        scaler = StandardScaler()
        X_scaled = scaler.fit_transform(X)

        # 模型训练
        kmeans = KMeans(n_clusters=3, random_state=44, n_init=10, tol=1e-7)
        df['cluster'] = kmeans.fit_predict(X_scaled)

        # 模型评估
        silhouette_avg = silhouette_score(X_scaled, kmeans.labels_)

        # 可视化（对比真实类别）
        plt.figure(figsize=(12, 6))

        # 子图1：聚类结果
        plt.subplot(1, 2, 1)
        sns.scatterplot(data=df, x='sepal_length', y='sepal_width',
                        hue='cluster', palette='viridis', s=100)
        plt.title(f'K-means Clustering\nSilhouette Score: {silhouette_avg:.2f}')

        # 子图2：真实类别（假设数据集有species列）
        plt.subplot(1, 2, 2)
        if 'species' in df.columns:
            sns.scatterplot(data=df, x='sepal_length', y='sepal_width',
                            hue='species', palette='tab10', s=100)
            plt.title('Actual Species Distribution')

        plt.tight_layout()

        # 保存结果
        img_path = os.path.join('.', 'static', 'results', 'img', 'kmeans_compare.png')
        plt.savefig(img_path)
        plt.close()

        return render(request, 'dsptool.html')
    else:
            return render(request, 'dsptool.html')

def csvtool(request):
    show_button_sign = 'data_button'
    image_paths = []
    #begin数据管理（2）这部分接收前端文件读取文件后产分出表头和表身，并将表头表身子存储在
    #会话当中返回表头表身给前端进行展示并附带一个信号变量show_button_sign,
    #该信号变量用于控制前端的相关按钮的显示与否的控制
    if request.method == 'POST' and request.FILES.get('csv_file'):
        csv_file = request.FILES['csv_file']
        decoded_file = csv_file.read().decode('utf-8').splitlines()
        reader = csv.reader(decoded_file)
        headers = next(reader)
        data = list(reader)

        # 将表头和数据存储在会话中
        request.session['headers'] = headers
        request.session['data'] = data
        return render(request, 'csvtool.html', {'headers': headers, 'data': data,'show_button_sign': show_button_sign})
    #end数据管理（2）

    if request.method == 'POST' and request.POST.get('columns'):
        action_type = request.POST['action_type']
        columns = request.POST.getlist('columns')

        # bengin数据清洗（1）
        #请忽略掉范围里可视化（通俗来讲就是生成图片）的代码
        # 前端发送携带标志信号的请求以及列选择的结果经过两个条件控制进入到该段代码，进行数据清洗，并将清洗好的数据发送回前端
        #并且将清洗好的数据的表头和表身覆盖保存到会话当中，供下面的文件下载使用
        # 分支对应上传文件后的数据项选择以及生成文件两个功能
        if action_type == 'data_columns':
            # 分支一
            print("选择的列：", columns)
            # 从会话中获取表头和数据
            headers = request.session.get('headers')
            data = request.session.get('data')

            # 创建数据框
            df = pd.DataFrame(data, columns=headers)

            # 处理数据类型：将数据列的可转换数据转换为float64
            for col in df.columns:
                if col in columns:
                    df[col] = pd.to_numeric(df[col], errors='coerce')
                    mean_value = df[col].mean()
                    df[col] = df[col].fillna(mean_value)

            # 缺失值处理：使用均值填充数值列的缺失值
            for col in df.columns:
                if pd.api.types.is_numeric_dtype(df[col]):
                    df[col] = pd.to_numeric(df[col], errors='coerce')
                    mean_value = df[col].mean()
                    df[col].fillna(mean_value, inplace=True)

            # 异常值检测和处理：基于标准差
            for col in df.columns:
                if pd.api.types.is_numeric_dtype(df[col]):
                    mean = df[col].mean()
                    std = df[col].std()
                    upper_bound = mean + 3 * std
                    lower_bound = mean - 3 * std
                    df[col] = df[col].apply(
                        lambda x: upper_bound if x > upper_bound else (lower_bound if x < lower_bound else x))

            # 将处理后的数据转换回列表
            data = df.values.tolist()

            # bengin可视化（1）
            #该部分比较简单，主要是了解一下相应绘图库的用法

            # 生成可视化散点图
            style_list = ['o', '^', 's']
            species = df['species'].unique()
            for each in [0, 2]:
                plt.figure()
                for i, spec in enumerate(species):
                    subset = df[df['species'] == spec]
                    plt.plot(subset.iloc[:, each], subset.iloc[:, each + 1], style_list[i])

                plt.title('sepal_length and sepal_width') if each else plt.title('petal_length and petal_width')
                plt.xlabel('sepal_length(cm)') if each else plt.xlabel('petal_length(cm)')
                plt.ylabel(' sepal_width(cm)') if each else plt.ylabel('petal_width(cm)')
                img_path = os.path.join('.', 'static', 'results', 'img', f'plot_{each}.png')
                plt.savefig(img_path)
                plt.close()
                image_paths.append(img_path)

            # 生成四张四维数据的直方图
            for i in range(4):
                plt.figure()
                for spec in species:
                    subset = df[df['species'] == spec]
                    plt.hist(subset.iloc[:, i], bins=30, alpha=0.5, label=spec)

                plt.title(f'Histogram of {headers[i]}')
                plt.xlabel(headers[i])
                plt.ylabel('Frequency')
                plt.legend()
                img_path = os.path.join('.', 'static', 'results', 'img', f'histogram_{i}.png')
                plt.savefig(img_path)
                plt.close()
                image_paths.append(img_path)

            # 生成箱型图
            for i in range(4):
                plt.figure()
                data_to_plot = [df[df['species'] == spec].iloc[:, i] for spec in species]
                plt.boxplot(data_to_plot, labels=species)

                plt.title(f'Box Plot of {headers[i]}')
                plt.xlabel('Species')
                plt.ylabel(headers[i])
                img_path = os.path.join('.', 'static', 'results', 'img', f'boxplot_{i}.png')
                plt.savefig(img_path)
                plt.close()
                image_paths.append(img_path)

            # end可视化（1）

            print(image_paths)
            # 将表头和数据存储在会话中
            request.session['headers'] = headers
            request.session['data'] = data
            show_button_sign = 'output_button'
            return render(request, 'csvtool.html', {'headers': headers, 'data': data,'show_button_sign': show_button_sign, 'image_paths': image_paths})
        # end数据清洗（1）

        # begin数据管理（4）
        #这部分接收存储在会话当中的处理好的文件然后将表头和数据写入一个新的csv文件，并且通过请求响应的相关字段唤起浏览器文件下载功能下载处理好的文件
        elif action_type == 'selected_columns':
            # 从会话中获取表头和数据
            headers = request.session.get('headers')
            data = request.session.get('data')

            selected_columns = request.POST.getlist('columns')
            selected_indices = [headers.index(col) for col in selected_columns]
            response = HttpResponse(content_type='text/csv')
            response['Content-Disposition'] = 'attachment; filename="new_file.csv"'
            writer = csv.writer(response)
            writer.writerow(selected_columns)
            for row in data:
                new_row = [row[i] for i in selected_indices]
                writer.writerow(new_row)
            return response
        # end数据管理（4）
    return render(request, 'csvtool.html')

def form_elements(request):
    return render(request, '../templates/csvtool.html')